#include <sys/types.h>
#include <stdlib.h>
#include "parse_uri.h"

#ifndef IS_ALPHA
#define IS_ALPHA(x)          (((x >= 'A') && (x <= 'Z')) || ((x >= 'a') && (x <= 'z')))
#endif

#ifndef IS_DIGIT
#define IS_DIGIT(x)          ((x >= '0') && (x <= '9'))
#endif

int parse_uri (const unsigned char *uri, size_t len, size_t *hostlen, unsigned short *port, const unsigned char **path, size_t *pathlen, unsigned int *ip)
{
	const unsigned char *ptr;
	const unsigned char *end;
	int state;
	int is_ip;
	int ndots;
	int n;
	int i;

	ptr = uri;
	end = uri + len;

	ndots = 0;

	if ((IS_ALPHA (*ptr)) || (*ptr == '_')) {
		is_ip = 0;
		i = 0; /* Just to remove warnings. */
	} else if (IS_DIGIT (*ptr)) {
		is_ip = 1;
		i = *ptr - '0';
		*ip = 0;
	} else {
		return -1;
	}

	ptr++;
	state = 0;
	n = 0;

	while (ptr < end) {
		switch (state) {
			case 0: /* Initial state. */
				if (*ptr == '-') {
					is_ip = 0;

					state = 1; /* After '-' or '.' */
				} else if (*ptr == '.') {
					if (is_ip) {
						*ip = (*ip * 256) + i;

						if (++ndots > 3) {
							is_ip = 0;
						}

						i = 0;
					}

					state = 1; /* After '-' or '.' */
				} else if ((IS_ALPHA (*ptr)) || (*ptr == '_')) {
					is_ip = 0;
				} else if (IS_DIGIT (*ptr)) {
					if (is_ip) {
						i = (i * 10) + (*ptr - '0');
						if (i > 255) {
							is_ip = 0;
						}
					}
				} else {
					if (*ptr == ':') {
						*hostlen = ptr - uri;
						state = 2; /* First digit of the port. */
					} else if (*ptr == '/') {
						*hostlen = ptr - uri;
						*port = 80;
						*path = ptr;
						*pathlen = end - ptr;

						if ((!is_ip) || (ndots != 3)) {
							*ip = 0;
						} else {
							*ip = (*ip * 256) + i;
						}

						return 0;
					} else {
						return -1;
					}
				}

				break;
			case 1: /* After '-' or '.' */
				if ((IS_ALPHA (*ptr)) || (*ptr == '_')) {
					is_ip = 0;
				} else if (IS_DIGIT (*ptr)) {
					if (is_ip) {
						i = *ptr - '0';
					}
				} else {
					return -1;
				}

				state = 0; /* Initial state. */
				break;
			case 2: /* First digit of the port. */
				if (!IS_DIGIT (*ptr)) {
					return -1;
				}

				n = *ptr - '0';

				state = 3; /* Port. */
				break;
			case 3: /* Port. */
				if (IS_DIGIT (*ptr)) {
					n = (n * 10) + (*ptr - '0');
				} else if (*ptr == '/') {
					if ((n < 1) || (n > 65535)) {
						return -1;
					}

					*port = n;
					*path = ptr;
					*pathlen = end - ptr;

					if ((!is_ip) || (ndots != 3)) {
						*ip = 0;
					} else {
						*ip = (*ip * 256) + i;
					}

					return 0;
				} else {
					return -1;
				}

				break;
		}

		ptr++;
	}

	return -1;
}
